Skip to content

feat: add Premium Geo DB addon to project settings#2981

Open
lohanidamodar wants to merge 21 commits into
mainfrom
feat/project-premium-geo-db-addon
Open

feat: add Premium Geo DB addon to project settings#2981
lohanidamodar wants to merge 21 commits into
mainfrom
feat/project-premium-geo-db-addon

Conversation

@lohanidamodar
Copy link
Copy Markdown
Member

Summary

Adds a Premium Geo DB section to the project settings page so users can enable and disable the premium geolocation addon per-project on cloud.

The section supports the full addon lifecycle, mirroring the BAA pattern at the organization level:

  • Upgrade prompt when the current plan does not support the addon
  • Enable flow with optional 3DS payment authentication
  • Pending state with cancel & retry option (if payment was interrupted)
  • Active state with disable action
  • Scheduled-for-removal state with re-enable action

Uses the new project-scoped SDK methods shipped with the cloud update: listAddons, createPremiumGeoDBAddon, and deleteAddon. Bumps the @appwrite.io/console SDK pin accordingly.

Test plan

  • Navigate to a cloud project's settings as an org owner on a plan that supports Premium Geo DB → section appears with Enable CTA
  • Click Enable → modal confirms and enables the addon (3DS where required); section transitions to Active
  • Click Disable → confirms removal at end of billing cycle; section transitions to Scheduled-for-removal
  • Click Keep Premium Geo DB while Scheduled-for-removal → returns to Active
  • On a plan that doesn't support the addon → shows Upgrade plan CTA / "not available" copy
  • On self-hosted mode → section is hidden

🤖 Generated with Claude Code

Adds a Premium Geo DB section to the project settings page so users
can enable and disable the premium geolocation addon per-project on
cloud. The section supports the full addon lifecycle:

- Upgrade prompt when the current plan does not support the addon
- Enable flow with optional 3DS payment authentication
- Pending state with cancel & retry option
- Active state with disable action
- Scheduled-for-removal state with re-enable action

Uses the new project-scoped SDK methods: listAddons, createPremiumGeoDBAddon,
and deleteAddon. Bumps the @appwrite.io/console SDK pin accordingly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 18, 2026

Greptile Summary

This PR adds a Premium Geo DB addon section to the project settings page, implementing the full enable/disable/pending/scheduled-for-removal lifecycle mirroring the existing BAA pattern at the org level. It also bumps the @appwrite.io/console SDK and updates planSummary.svelte to surface per-project addon charges in the billing breakdown.

  • premiumGeoDB.svelte: New component with onMount 3DS return-URL handler, reactive state machine for all addon states, and handleReEnable/handleRefresh actions; the isPending branch renders only a "Refresh" button with no way to cancel a stuck pending payment (see inline comment), and handleReEnable discards the SDK response so 3DS re-enables are silently incomplete.
  • premiumGeoDBEnableModal.svelte: Handles the initial enable + 3DS branch; confirmPayment is called without redirectIfRequired: true and its outcome object is not inspected for error or requires_action, leaving users without feedback when a card challenge fails.
  • planSummary.svelte: Removes the hardcoded billingAddonNames fallback (BAA display name now depends entirely on the API's name field) and adds per-project addon rows that may double-count charges already rendered by the top-level addons section.

Confidence Score: 3/5

Multiple addon lifecycle flows leave users in irrecoverable states or with silent failures; hold until the pending-cancel gap and the 3DS handling issues are resolved.

The pending-payment state has no cancel action, so a user whose payment confirmation keeps failing is permanently blocked from enabling or disabling the addon without support intervention. handleReEnable silently drops the SDK return value, meaning 3DS re-enables never initiate a payment challenge and the addon stays in a broken state. The enable modal's confirmPayment call is missing redirectIfRequired: true and does not inspect the outcome, causing unnecessary full-page redirects and silent failures on 3DS errors. Together these issues cover several of the six core lifecycle paths described in the PR.

premiumGeoDB.svelte (pending-cancel gap, handleReEnable 3DS handling) and premiumGeoDBEnableModal.svelte (confirmPayment options and outcome inspection) need the most attention before this ships.

Important Files Changed

Filename Overview
src/routes/(console)/project-[region]-[project]/settings/premiumGeoDB.svelte New component managing the full Premium Geo DB addon lifecycle; onMount 3DS handler is now present, but the pending state has no cancel action, and handleReEnable discards the createPremiumGeoDBAddon return value so 3DS re-enables are silently broken.
src/routes/(console)/project-[region]-[project]/settings/premiumGeoDBEnableModal.svelte New enable modal; missing redirectIfRequired: true in confirmPayment causes full-page redirect for every payment, and the confirmPayment outcome object is not inspected for error/requires_action status.
src/routes/(console)/project-[region]-[project]/settings/premiumGeoDBDisableModal.svelte New disable modal; straightforward deleteAddon call with error handling, bindable show prop, and success notification — no issues found.
src/routes/(console)/project-[region]-[project]/settings/+page.ts Adds listAddons and getAddonPrice calls under an isCloud guard using .catch(() => null) for graceful fallback; Dependencies.ADDONS dependency key wired correctly.
src/routes/(console)/project-[region]-[project]/settings/+page.svelte Adds PremiumGeoDB component under isCloud && $canWriteProjects guard — gating is correct.
src/routes/(console)/organization-[organization]/billing/planSummary.svelte Removes hardcoded billingAddonNames map (BAA fallback name gone) and adds per-project addon rows inside the project breakdown — double-counting risk with the top-level addons section remains unresolved.

Reviews (18): Last reviewed commit: "fix(settings): handle 3DS redirect + rep..." | Re-trigger Greptile

lohanidamodar and others added 4 commits April 19, 2026 11:13
Mirrors the BAA addon UX by fetching the addon price via
organizations.getAddonPrice(Addon.Premiumgeodb) from the settings
page loader, passing it through to the Premium Geo DB card and
enable modal, and rendering the monthly/prorated breakdown with
formatCurrency alongside the Enable CTA.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…name

- Each project row in the organization billing breakdown now iterates
  its resources for addon_* entries (amount > 0) and renders them as
  child rows (e.g. Premium Geo DB under the project it was enabled on).
  The backend already filters project-scoped addons out of the
  team-level resources response, so the org "Addons" section shows only
  org-scoped addons (BAA, premiumGeoDBOrg) while project-scoped ones
  surface where they belong.
- Org-level addon labels now read addon.name from the UsageResource
  payload that the getAggregation endpoint populates from billingAddons
  config. Dropped the hard-coded billingAddonNames map so new addons
  surface with their proper name without a console update.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…geo-db-addon

# Conflicts:
#	bun.lock
#	package.json
#	src/routes/(console)/project-[region]-[project]/settings/+page.svelte
…geo-db-addon

# Conflicts:
#	bun.lock
#	package.json
…geo-db-addon

# Conflicts:
#	src/routes/(console)/project-[region]-[project]/settings/+page.svelte
#	src/routes/(console)/project-[region]-[project]/settings/+page.ts
Comment on lines +399 to +410
...resources
.filter((r) => r.resourceId?.startsWith('addon_') && (r.amount ?? 0) > 0)
.map((addon) =>
createRow({
id: `addon-${addon.resourceId}`,
label: addon.name || addon.resourceId,
resource: addon,
usageFormatter: ({ value }) => formatNum(value),
priceFormatter: ({ amount }) => formatCurrency(amount),
includeProgress: false
})
),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Possible double-counting of addon charges in plan summary

The top-level addons section (line 252–275) already creates a billing row for every addon_-prefixed resource found in currentAggregation.resources. If the billing API also surfaces those same addon resources inside projectData.resources (the per-project breakdown), each project-scoped addon (e.g. Premium Geo DB) will appear as both a top-level line item and a child row — showing the same charge twice to the user.

Before shipping, confirm whether the cloud billing API places project-level addon charges exclusively in breakdown[].resources (making the top-level filter skip them) or in both places. If the former, this code is correct; if the latter, the top-level addons filter needs to exclude resources that are already accounted for at the project level (or vice-versa).

…h for premium geoDB

Two missing pieces vs the BAA addon flow:

1. After Stripe redirects back from 3DS with ?type=confirm-addon&addonId=...,
   the page didn't read the query params or call the confirmations endpoint,
   so the addon stayed pending forever. Port the onMount handler from BAA.svelte.

2. The pending-state action was "Cancel & retry" which deleted the addon and
   forced a fresh 3DS flow. That's wasteful when the payment just needs a
   server-side status sync. Replace with "Refresh" — calls the confirmations
   endpoint, which live-queries Stripe and activates the addon if the
   PaymentIntent has transitioned to succeeded. Backend cleans up on 402.
Comment on lines +184 to +198
{:else if isPending}
<div class="u-flex u-cross-center u-gap-8 u-margin-block-start-8">
<Badge variant="secondary" type="warning" content="Payment pending" />
</div>
<p class="text u-margin-block-start-8">
A payment is awaiting confirmation. If you've completed authentication, click
refresh to check the payment status.
</p>
<Button
secondary
class="u-margin-block-start-16"
disabled={refreshing}
on:click={handleRefresh}>
<span class="text">Refresh</span>
</Button>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 No escape route for stuck pending addons

When the addon is in pending state the user only sees a "Refresh" button. If confirmAddonPayment keeps returning an error (e.g., persistent 402 or a network issue), the user is trapped: the "Enable" CTA is hidden (the branch falls to isPending first), and there is no way to cancel the pending payment and start over. The PR description's own test plan calls this out as "Pending state with cancel & retry option (if payment was interrupted)", but no cancel action is wired up.

A "Cancel" button that calls deleteAddon (same SDK method used by the disable modal) should be added next to "Refresh" so users can abort a stuck pending payment and re-enter the enable flow from a clean state.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant